home *** CD-ROM | disk | FTP | other *** search
Python egg package info | 2009-08-25 | 24.5 KB | 599 lines |
- Metadata-Version: 1.0
- Name: wadllib
- Version: 1.1.2
- Summary: Navigate HTTP resources using WADL files as guides.
- Home-page: https://launchpad.net/wadllib
- Author: LAZR Developers
- Author-email: lazr-developers@lists.launchpad.net
- License: LGPL v3
- Download-URL: https://launchpad.net/wadllib/+download
- Description: ..
- Copyright (C) 2008-2009 Canonical Ltd.
-
- This file is part of wadllib.
-
- wadllib is free software: you can redistribute it and/or modify it under
- the terms of the GNU Lesser General Public License as published by the
- Free Software Foundation, version 3 of the License.
-
- wadllib is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
- more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with wadllib. If not, see <http://www.gnu.org/licenses/>.
-
- wadllib
- *******
-
- An Application object represents a web service described by a WADL
- file.
-
- >>> import os
- >>> import sys
- >>> import pkg_resources
- >>> from wadllib.application import Application
-
- The first argument to the Application constructor is the URL at which
- the WADL file was found. The second argument may be raw WADL markup.
-
- >>> wadl_string = pkg_resources.resource_string(
- ... 'wadllib.tests.data', 'launchpad-wadl.xml')
- >>> wadl = Application("http://api.launchpad.dev/beta/", wadl_string)
-
- Or the second argument may be an open filehandle containing the markup.
-
- >>> def application_for(filename, url="http://www.example.com/"):
- ... wadl_stream = pkg_resources.resource_stream(
- ... 'wadllib.tests.data', filename)
- ... return Application(url, wadl_stream)
- >>> wadl = application_for("launchpad-wadl.xml",
- ... "http://api.launchpad.dev/beta/")
-
-
- ===============
- Link navigation
- ===============
-
- The preferred technique for finding a resource is to start at one of
- the resources defined in the WADL file, and follow links. This code
- retrieves the definition of the root resource.
-
- >>> service_root = wadl.get_resource_by_path('')
- >>> service_root.url
- 'http://api.launchpad.dev/beta/'
- >>> service_root.type_url
- '#service-root'
-
- The service root resource supports GET.
-
- >>> get_method = service_root.get_method('get')
- >>> get_method.id
- 'service-root-get'
-
- >>> get_method = service_root.get_method('GET')
- >>> get_method.id
- 'service-root-get'
-
- If we want to invoke this method, we send a GET request to the service
- root URL.
-
- >>> get_method.name
- 'get'
- >>> get_method.build_request_url()
- 'http://api.launchpad.dev/beta/'
-
- The WADL description of a resource knows which representations are
- available for that resource. In this case, the server root resource
- has a a JSON representation, and it defines parameters like
- 'people_collection_link', a link to a list of people in Launchpad. We
- should be able to use the get_parameter() method to get the WADL
- definition of the 'people_collection_link' parameter and find out more
- about it--for instance, is it a link to another resource?
-
- >>> link_name = 'people_collection_link'
- >>> link_parameter = service_root.get_parameter(link_name)
- Traceback (most recent call last):
- ...
- NoBoundRepresentationError: Resource is not bound to any representation, and no media media type was specified.
-
- Oops. The code has no way to know whether 'people_collection_link' is
- a parameter of the JSON representation or some other kind of
- representation. We can pass a media type to get_parameter and let it
- know which representation the parameter lives in.
-
- >>> link_parameter = service_root.get_parameter(
- ... link_name, 'application/json')
- >>> link_parameter.get_value()
- Traceback (most recent call last):
- ...
- NoBoundRepresentationError: Resource is not bound to any representation.
-
- Oops again. The parameter is available, but it has no value, because
- there's no actual data associated with the resource. The browser can
- look up the description of the GET method to make an actual GET
- request to the service root, and bind the resulting representation to
- the WADL description of the service root.
-
- You can't bind just any representation to a WADL resource description.
- It has to be of a media type understood by the WADL description.
-
- >>> service_root.bind('<html>Some HTML</html>', 'text/html')
- Traceback (most recent call last):
- ...
- UnsupportedMediaTypeError: This resource doesn't define a representation for media type text/html
-
- The WADL description of the service root resource has a JSON
- representation. Here it is.
-
- >>> json_representation = service_root.get_representation_definition(
- ... 'application/json')
- >>> json_representation.media_type
- 'application/json'
-
- We already have a WADL representation of the service root resource, so
- let's try binding it to that JSON representation. We use test JSON
- data from a file to simulate the result of a GET request to the
- service root.
-
- >>> def get_testdata(filename):
- ... return pkg_resources.resource_string(
- ... 'wadllib.tests.data', filename + '.json')
-
- >>> def bind_to_testdata(resource, filename):
- ... return resource.bind(get_testdata(filename), 'application/json')
-
- The return value is a new Resource object that's "bound" to that JSON
- test data.
-
- >>> bound_service_root = bind_to_testdata(service_root, 'root')
- >>> sorted(bound_service_root.parameter_names())
- ['bugs_collection_link', 'people_collection_link']
- >>> [method.id for method in bound_service_root.method_iter]
- ['service-root-get']
-
-
- Now the bound resource object has a JSON representation, and now
- 'people_collection_link' makes sense. We can follow the
- 'people_collection_link' to a new Resource object.
-
- >>> link_parameter = bound_service_root.get_parameter(link_name)
- >>> link_parameter.style
- 'plain'
- >>> link_parameter.get_value()
- u'http://api.launchpad.dev/beta/people'
- >>> personset_resource = link_parameter.linked_resource
- >>> personset_resource.__class__
- <class 'wadllib.application.Resource'>
- >>> personset_resource.url
- u'http://api.launchpad.dev/beta/people'
- >>> personset_resource.type_url
- 'http://api.launchpad.dev/beta/#people'
-
- This new resource is a collection of people.
-
- >>> personset_resource.id
- 'people'
-
- The "collection of people" resource supports a standard GET request as
- well as a special GET and an overloaded POST. The get_method() method
- is used to retrieve WADL definitions of the possible HTTP requests you
- might make. Here's how to get the WADL definition of the standard GET
- request.
-
- >>> get_method = personset_resource.get_method('get')
- >>> get_method.id
- 'people-get'
-
- The method name passed into get_method() is treated case-insensitively.
-
- >>> personset_resource.get_method('GET').id
- 'people-get'
-
- To invoke the special GET request, the client sets the 'ws.op' query
- parameter to the fixed string 'find'.
-
- >>> find_method = personset_resource.get_method(
- ... query_params={'ws.op' : 'findPerson'})
- >>> find_method.id
- 'people-findPerson'
-
- Given an end-user's values for the non-fixed parameters, it's possible
- to get the URL that should be used to invoke the method.
-
- >>> find_method.build_request_url(text='foo')
- u'http://api.launchpad.dev/beta/people?text=foo&ws.op=findPerson'
-
- >>> find_method.build_request_url({'ws.op' : 'findPerson', 'text' : 'bar'})
- u'http://api.launchpad.dev/beta/people?text=bar&ws.op=findPerson'
-
- An error occurs if the end-user gives an incorrect value for a fixed
- parameter value, or omits a required parameter.
-
- >>> find_method.build_request_url()
- Traceback (most recent call last):
- ...
- ValueError: No value for required parameter 'text'
-
- >>> find_method.build_request_url(
- ... {'ws.op' : 'findAPerson', 'text' : 'foo'})
- ... # doctest: +ELLIPSIS, +NORMALIZE_WHITESPACE
- Traceback (most recent call last):
- ...
- ValueError: Value 'findAPerson' for parameter 'ws.op' conflicts
- with fixed value 'findPerson'
-
- To invoke the overloaded POST request, the client sets the 'ws.op'
- query variable to the fixed string 'newTeam':
-
- >>> create_team_method = personset_resource.get_method(
- ... 'post', representation_params={'ws.op' : 'newTeam'})
- >>> create_team_method.id
- 'people-newTeam'
-
- findMethod() returns None when there's no WADL method matching the
- name or the fixed parameters.
-
- >>> print personset_resource.get_method('nosuchmethod')
- None
-
- >>> print personset_resource.get_method(
- ... 'post', query_params={'ws_op' : 'nosuchparam'})
- None
-
- Let's say the browser makes a GET request to the person set resource
- and gets back a representation. We can bind that representation to our
- description of the person set resource.
-
- >>> bound_personset = bind_to_testdata(personset_resource, 'personset')
- >>> bound_personset.get_parameter("start").get_value()
- 0
- >>> bound_personset.get_parameter("total_size").get_value()
- 63
-
- We can keep following links indefinitely, so long as we bind to a
- representation to each resource as we get it, and use the
- representation to find the next link.
-
- >>> next_page_link = bound_personset.get_parameter("next_collection_link")
- >>> next_page_link.get_value()
- u'http://api.launchpad.dev/beta/people?ws.start=5&ws.size=5'
- >>> page_two = next_page_link.linked_resource
- >>> bound_page_two = bind_to_testdata(page_two, 'personset-page2')
- >>> bound_page_two.url
- u'http://api.launchpad.dev/beta/people?ws.start=5&ws.size=5'
- >>> bound_page_two.get_parameter("start").get_value()
- 5
- >>> bound_page_two.get_parameter("next_collection_link").get_value()
- u'http://api.launchpad.dev/beta/people?ws.start=10&ws.size=5'
-
- Let's say the browser makes a POST request that invokes the 'newTeam'
- named operation. The response will include a number of HTTP headers,
- including 'Location', which points the way to the newly created team.
-
- >>> headers = { 'Location' : 'http://api.launchpad.dev/~newteam' }
- >>> response = create_team_method.response.bind(headers)
- >>> location_parameter = response.get_parameter('Location')
- >>> location_parameter.get_value()
- 'http://api.launchpad.dev/~newteam'
- >>> new_team = location_parameter.linked_resource
- >>> new_team.url
- 'http://api.launchpad.dev/~newteam'
- >>> new_team.type_url
- 'http://api.launchpad.dev/beta/#team'
-
- ======================
- Resource instantiation
- ======================
-
- If you happen to have the URL to an object lying around, and you know
- its type, you can construct a Resource object directly instead of
- by following links.
-
- >>> from wadllib.application import Resource
- >>> limi_person = Resource(wadl, "http://api.launchpad.dev/beta/~limi",
- ... "http://api.launchpad.dev/beta/#person")
- >>> sorted([method.id for method in limi_person.method_iter])[:3]
- ['person-acceptInvitationToBeMemberOf', 'person-addMember', 'person-declineInvitationToBeMemberOf']
-
- >>> bound_limi = bind_to_testdata(limi_person, 'person-limi')
- >>> sorted(bound_limi.parameter_names())[:3] # doctest: +NORMALIZE_WHITESPACE
- ['admins_collection_link', 'confirmed_email_addresses_collection_link',
- 'date_created']
- >>> languages_link = bound_limi.get_parameter("languages_collection_link")
- >>> languages_link.get_value()
- u'http://api.launchpad.dev/beta/~limi/languages'
-
- You can bind a Resource to a representation when you create it.
-
- >>> limi_data = get_testdata('person-limi')
- >>> bound_limi = Resource(
- ... wadl, "http://api.launchpad.dev/beta/~limi",
- ... "http://api.launchpad.dev/beta/#person", limi_data,
- ... "application/json")
- >>> bound_limi.get_parameter("languages_collection_link").get_value()
- u'http://api.launchpad.dev/beta/~limi/languages'
-
- By default the representation is treated as a string and processed
- according to the media type you pass into the Resource constructor. If
- you've already processed the representation, pass in False for the
- 'representation_needs_processing' argument.
-
- >>> import simplejson
- >>> processed_limi_data = simplejson.loads(unicode(limi_data))
- >>> bound_limi = Resource(wadl, "http://api.launchpad.dev/beta/~limi",
- ... "http://api.launchpad.dev/beta/#person", processed_limi_data,
- ... "application/json", False)
- >>> bound_limi.get_parameter("languages_collection_link").get_value()
- u'http://api.launchpad.dev/beta/~limi/languages'
-
- Most of the time, the representation of a resource is of the type
- you'd get by sending a standard GET to that resource. If that's not
- the case, you can specify a RepresentationDefinition as the
- 'representation_definition' argument to bind() or the Resource
- constructor, to show what the representation really looks like. Here's
- an example.
-
- There's a method on a person resource such as bound_limi that's
- identified by a distinctive query argument: ws.op=getMembersByStatus.
-
- >>> method = bound_limi.get_method(
- ... query_params={'ws.op' : 'findPathToTeam'})
-
- Invoke this method with a GET request and you'll get back a page from
- a list of people.
-
- >>> people_page_repr_definition = (
- ... method.response.get_representation_definition('application/json'))
- >>> people_page_repr_definition.tag.attrib['href']
- 'http://api.launchpad.dev/beta/#person-page'
-
- As it happens, we have a page from a list of people to use as test data.
-
- >>> people_page_repr = get_testdata('personset')
-
- If we bind the resource to the result of the method invocation as
- happened above, we don't be able to access any of the parameters we'd
- expect. wadllib will think the representation is of type
- 'person-full', the default GET type for bound_limi.
-
- >>> bad_people_page = bound_limi.bind(people_page_repr)
- >>> print bad_people_page.get_parameter('total_size')
- None
-
- Since we don't actually have a 'person-full' representation, we won't
- be able to get values for the parameters of that kind of
- representation.
-
- >>> bad_people_page.get_parameter('name').get_value()
- Traceback (most recent call last):
- ...
- KeyError: 'name'
-
- So that's a dead end. *But*, if we pass the correct representation
- type into bind(), we can access the parameters associated with a
- 'person-page' representation.
-
- >>> people_page = bound_limi.bind(
- ... people_page_repr,
- ... representation_definition=people_page_repr_definition)
- >>> people_page.get_parameter('total_size').get_value()
- 63
-
- If you invoke the method and ask for a media type other than JSON, you
- won't get anything.
-
- >>> print method.response.get_representation_definition('text/html')
- None
-
- Data type conversion
- ====================
-
- The values of date and dateTime parameters are automatically converted to
- Python datetime objects.
-
- >>> data_type_wadl = application_for('data-types-wadl.xml')
- >>> service_root = data_type_wadl.get_resource_by_path('')
-
- >>> representation = simplejson.dumps(
- ... {'a_date': '2007-10-20',
- ... 'a_datetime': '2005-06-06T08:59:51.619713+00:00'})
- >>> bound_root = service_root.bind(representation, 'application/json')
-
- >>> bound_root.get_parameter('a_date').get_value()
- datetime.datetime(2007, 10, 20, 0, 0)
- >>> bound_root.get_parameter('a_datetime').get_value() # doctest: +ELLIPSIS
- datetime.datetime(2005, 6, 6, 8, ...)
-
- A 'date' field can include a timestamp, and a 'datetime' field can
- omit one. wadllib will turn both into datetime objects.
-
- >>> representation = simplejson.dumps(
- ... {'a_date': '2005-06-06T08:59:51.619713+00:00',
- ... 'a_datetime': '2007-10-20'})
- >>> bound_root = service_root.bind(representation, 'application/json')
-
- >>> bound_root.get_parameter('a_datetime').get_value()
- datetime.datetime(2007, 10, 20, 0, 0)
- >>> bound_root.get_parameter('a_date').get_value() # doctest: +ELLIPSIS
- datetime.datetime(2005, 6, 6, 8, ...)
-
- If a date or dateTime parameter has a null value, you get None. If the
- value is a string that can't be parsed to a datetime object, you get a
- ValueError.
-
- >>> representation = simplejson.dumps(
- ... {'a_date': 'foo', 'a_datetime': None})
- >>> bound_root = service_root.bind(representation, 'application/json')
- >>> bound_root.get_parameter('a_date').get_value()
- Traceback (most recent call last):
- ...
- ValueError: foo
- >>> print bound_root.get_parameter('a_datetime').get_value()
- None
-
- =======================
- Representation creation
- =======================
-
- You must provide a representation when invoking certain methods. The
- representation() method helps you build one without knowing the
- details of how a representation is put together.
-
- >>> create_team_method.build_representation(
- ... display_name='Joe Bloggs', name='joebloggs')
- ('application/x-www-form-urlencoded', 'display_name=Joe+Bloggs&ws.op=newTeam&name=joebloggs')
-
- The return value of build_representation is a 2-tuple containing the
- media type of the built representation, and the string representation
- itself. Along with the resource's URL, this is all you need to send
- the representation to a web server.
-
- >>> bound_limi.get_method('patch').build_representation(name='limi2')
- ('application/json', '{"name": "limi2"}')
-
- Representations may require values for certain parameters.
-
- >>> create_team_method.build_representation()
- Traceback (most recent call last):
- ...
- ValueError: No value for required parameter 'display_name'
-
- >>> bound_limi.get_method('put').build_representation(name='limi2')
- Traceback (most recent call last):
- ...
- ValueError: No value for required parameter 'mugshot_link'
-
- Some representations may safely include binary data.
-
- >>> binary_stream = pkg_resources.resource_stream(
- ... 'wadllib.tests.data', 'multipart-binary-wadl.xml')
- >>> binary_wadl = Application(
- ... "http://www.example.com/", binary_stream)
- >>> service_root = binary_wadl.get_resource_by_path('')
-
- >>> method = service_root.get_method('post', 'multipart/form-data')
- >>> media_type, doc = method.build_representation(
- ... text_field="text", binary_field="\x01\x02")
- >>> print media_type
- multipart/form-data; boundary=...
- >>> print doc
- MIME-Version: 1.0
- Content-Type: multipart/form-data; boundary="..."
- <BLANKLINE>
- --...
- Content-Type: text/plain; charset="utf-8"
- MIME-Version: 1.0
- Content-Disposition: form-data; name="text_field"
- <BLANKLINE>
- text
- --...
- Content-Type: application/octet-stream
- MIME-Version: 1.0
- Content-Disposition: form-data; name="binary_field"
- <BLANKLINE>
- ...
- --...
- >>> '\x01\x02' in doc
- True
-
- >>> method = service_root.get_method('post', 'text/unknown')
- >>> method.build_representation(field="value")
- Traceback (most recent call last):
- ...
- ValueError: Unsupported media type: 'text/unknown'
-
- =======
- Options
- =======
-
- Some parameters take values from a predefined list of options.
-
- >>> option_wadl = application_for('options-wadl.xml')
- >>> definitions = option_wadl.representation_definitions
- >>> service_root = option_wadl.get_resource_by_path('')
- >>> definition = definitions['service-root-json']
- >>> param = definition.params(service_root)[0]
- >>> print param.name
- has_options
- >>> sorted([option.value for option in param.options])
- ['Value 1', 'Value 2']
-
- Such parameters cannot take values that are not in the list.
-
- >>> definition.validate_param_values(
- ... [param], {'has_options': 'Value 1'})
- {'has_options': 'Value 1'}
-
- >>> definition.validate_param_values(
- ... [param], {'has_options': 'Invalid value'})
- Traceback (most recent call last):
- ...
- ValueError: Invalid value 'Invalid value' for parameter
- 'has_options': valid values are: "Value 1", "Value 2"
-
-
- ================
- Error conditions
- ================
-
- You'll get None if you try to look up a nonexistent resource.
-
- >>> print wadl.get_resource_by_path('nosuchresource')
- None
-
- You'll get an exception if you try to look up a nonexistent resource
- type.
-
- >>> print wadl.get_resource_type('#nosuchtype')
- Traceback (most recent call last):
- KeyError: 'No such XML ID: "#nosuchtype"'
-
- You'll get None if you try to look up a method whose parameters don't
- match any defined method.
-
- >>> print bound_limi.get_method(
- ... 'post', representation_params={ 'foo' : 'bar' })
- None
-
- .. toctree::
- :glob:
-
- *
- docs/*
-
- ================
- NEWS for wadllib
- ================
-
- 1.1.2 (2009-08-20)
- ==================
-
- - Consistently handle different versions of simplejson.
-
- 1.1.1 (2009-07-14)
- ==================
-
- - Make wadllib aware of the <option> tags that go beneath <param> tags.
-
- 1.1 (2009-07-09)
- ================
-
- - Make wadllib capable of recognizing and generating
- multipart/form-data representations, including representations that
- incorporate binary parameters.
-
-
- 1.0 (2009-03-23)
- ================
-
- - Initial release on PyPI
-
- Platform: UNKNOWN
- Classifier: Development Status :: 5 - Production/Stable
- Classifier: Intended Audience :: Developers
- Classifier: License :: OSI Approved :: GNU Library or Lesser General Public License (LGPL)
- Classifier: Operating System :: OS Independent
- Classifier: Programming Language :: Python
-